﻿#region File Info Header
/*________________________________________________________________________________________

  Copyright (C) 2011 Jason Zhang, eagleboost@msn.com

  * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
  * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED 
  * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

________________________________________________________________________________________*/
#endregion File Info Header

namespace EagleBoost.Wpf.Presentation.Controls
{
  using System;
  using System.Windows;
  using System.Windows.Controls;
  using Extensions;

  /// <summary>
  /// AutoLayoutGrid - A simple extension for the Grid control that:
  /// 1. Given a static ColumnDefinitions (Vertical orientation), it automatically place one child per cell from (0,0)->(0,1)...->(n-1, m-1).
  ///    New rows will be created automatically depends on # of children
  /// 2. Given a static RowDefinitions (Horizontal orientation), it automatically place one child per cell from (0,0)->(1,0)...->(n-1, m-1).
  ///    New columns will be created automatically depends on # of children
  /// Particularly, it's used in controls like color picker to arrange a lot of controls easily
  /// </summary>
  public class AutoLayoutGrid : Grid
  {
    #region Dependency Properties
    public static readonly DependencyProperty ChildMarginProperty =
        DependencyProperty.Register("ChildMargin", typeof(Thickness?), typeof(AutoLayoutGrid),
        new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsMeasure, PropertyChanged));

    /// <summary>
    /// Arrange Orientation, Vertical by default
    /// </summary>
    public Thickness? ChildMargin
    {
      get { return (Thickness?)GetValue(ChildMarginProperty); }
      set { SetValue(ChildMarginProperty, value); }
    }

    public static readonly DependencyProperty ChildHorizontalAlignmentProperty =
        DependencyProperty.Register("ChildHorizontalAlignment", typeof(HorizontalAlignment?), typeof(AutoLayoutGrid),
        new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsMeasure, PropertyChanged));

    /// <summary>
    /// Child's HorizontalAlignment, Center by default
    /// </summary>
    public HorizontalAlignment? ChildHorizontalAlignment
    {
      get { return (HorizontalAlignment?)GetValue(ChildHorizontalAlignmentProperty); }
      set { SetValue(ChildHorizontalAlignmentProperty, value); }
    }

    public static readonly DependencyProperty ChildVerticalAlignmentProperty =
        DependencyProperty.Register("ChildVerticalAlignment", typeof(VerticalAlignment?), typeof(AutoLayoutGrid),
        new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsMeasure, PropertyChanged));

    /// <summary>
    /// Child's VerticalAlignment, Center by default
    /// </summary>
    public VerticalAlignment? ChildVerticalAlignment
    {
      get { return (VerticalAlignment?)GetValue(ChildVerticalAlignmentProperty); }
      set { SetValue(ChildVerticalAlignmentProperty, value); }
    }

    public static readonly DependencyProperty OrientationProperty =
        DependencyProperty.Register("Orientation", typeof(Orientation), typeof(AutoLayoutGrid),
        new FrameworkPropertyMetadata(Orientation.Vertical,
          FrameworkPropertyMetadataOptions.AffectsMeasure, PropertyChanged));

    /// <summary>
    /// Arrange Orientation, Vertical by default
    /// </summary>
    public Orientation Orientation
    {
      get { return (Orientation)GetValue(OrientationProperty); }
      set { SetValue(OrientationProperty, value); }
    }
    #endregion Dependency Properties

    #region Private Properties
    internal bool RearrangeRequired { get; set; }
    #endregion Private Properties

    #region Private Methods
    private void RearrangeVertical(Action<UIElement> childUpdateAction)
    {
      //Clear row definitions
      RowDefinitions.Clear();

      var position = 0;
      foreach (UIElement child in Children)
      {
        var rowIndex = position / ColumnDefinitions.Count;
        if (rowIndex == RowDefinitions.Count)
        {
          RowDefinitions.Add(new RowDefinition());
        }

        SetRow(child, rowIndex);
        SetColumn(child, position % ColumnDefinitions.Count);
        childUpdateAction(child);

        position += GetColumnSpan(child);
      }
    }

    private void RearrangeHorizontal(Action<UIElement> childUpdateAction)
    {
      //Clear column definitions
      ColumnDefinitions.Clear();

      var position = 0;
      foreach (UIElement child in Children)
      {
        var columnIndex = position / RowDefinitions.Count;
        if (columnIndex == ColumnDefinitions.Count)
        {
          ColumnDefinitions.Add(new ColumnDefinition());
        }

        SetRow(child, position % RowDefinitions.Count);
        SetColumn(child, columnIndex);
        childUpdateAction(child);

        position += GetRowSpan(child);
      }
    }
    #endregion Private Methods

    #region Overrides
    protected override Size MeasureOverride(Size constraint)
    {
      if (RearrangeRequired)
      {
        RearrangeRequired = false;

        Action<UIElement> updateChild =
          child =>
          {
            child.SetValueIfDefault(MarginProperty, ChildMargin);
            child.SetValueIfDefault(HorizontalAlignmentProperty, ChildHorizontalAlignment);
            child.SetValueIfDefault(VerticalAlignmentProperty, ChildVerticalAlignment);
            child.Measure(constraint);
          };

        if (Orientation == Orientation.Vertical)
        {
          RearrangeVertical(updateChild);
        }
        else
        {
          RearrangeHorizontal(updateChild);
        }
      }

      return base.MeasureOverride(constraint);
    }

    protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
    {
      RearrangeRequired = true;
      base.OnVisualChildrenChanged(visualAdded, visualRemoved);
    }
    #endregion Overrides

    #region Event Handlers
    private static void PropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    {
      ((AutoLayoutGrid)obj).RearrangeRequired = true;
    }
    #endregion Event Handlers
  }
}
